package jmauth

import (
	"crypto/sha1"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	conf "epg-mc/config"

	session "github.com/ipfans/echo-session"
	"github.com/labstack/echo"
)

var config *authConfig

var errNoPriv = errors.New("have no privilege")

func init() {
	config = newAuthConfig(conf.Conf.Auth.AppName, conf.Conf.Auth.AppKey, conf.Conf.Auth.AuthHost, conf.Conf.Auth.CallbackURL)
}

type JMAuth struct {
}

func NewJMAuth() *JMAuth {
	return &JMAuth{}
}

// Process is the middleware function.
func (s *JMAuth) Process(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		sess := session.Default(c)
		uname, _ := sess.Get("uname").(string)
		sessID, _ := sess.Get("sessid").(string)
		token, _ := sess.Get("token").(string)
		group, _ := sess.Get("groups").([]string)
		url := c.Request().URL().Path()

		//check login
		if sessID != "" && uname != "" && token != "" {
			if sessID == getSessID(token, uname) {
				c.Set("uname", uname)
				if !config.hasPriv(group, url) {
					return errNoPriv
				}
				return next(c)
			}
			return errors.New("Auth faild")
		}
		token = c.QueryParam("token")
		uname = c.QueryParam("username")
		// log.Printf("=====DEBUG======, token:%s, username:%s \n", token, uname)
		if token != "" && uname != "" {
			//auth callback, validate the token
			sessID = getSessID(token, uname)
			info, err := getInfo(sessID)
			if err != nil {
				return err
			}
			group, err := getGroup(uname)
			if err != nil {
				return err
			}
			if !config.hasPriv(group, url) {
				return errNoPriv
			}

			sess.Set("token", token)
			sess.Set("uname", uname)
			sess.Set("sessid", sessID)
			sess.Set("groups", group)
			err = sess.Save()
			log.Printf("user login: %+v, group:%v\n", info, group)
			return next(c)
		}
		c.Redirect(http.StatusMovedPermanently, config.getAuthURL())
		return nil
	}
}

//validate validate the token and return the user info
func getSessID(token, uname string) string {
	return fmt.Sprintf("%x", sha1.Sum([]byte(token+conf.Conf.Auth.AppKey+uname)))
}

type infoResp struct {
	ErrorID  int
	ErrorMsg string
	UserName string
	Mail     string
	FullName string
}

func getInfo(sessID string) (*infoResp, error) {
	url := config.getInfoURL(sessID)
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	m := infoResp{}
	err = json.Unmarshal(body, &m)
	if err != nil {
		return nil, err
	}
	if m.ErrorID > 0 {
		return nil, fmt.Errorf(m.ErrorMsg)
	}
	return &m, nil
}

func getGroup(uname string) ([]string, error) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	resp, err := client.Get(config.getPrivURL(uname))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	m := make(map[string]interface{})
	err = json.Unmarshal(body, &m)
	if err != nil {
		return nil, err
	}
	r, ok := m["groups"].([]interface{})
	if !ok {
		return nil, errors.New("asset groups faild")
	}
	rst := make([]string, 0, 4)
	for _, v := range r {
		rst = append(rst, v.(string))
	}
	return rst, nil
}
